/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
 
package com.cburch.logisim.analyze.gui;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.AbstractListModel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.cburch.logisim.analyze.model.VariableList;
import com.cburch.logisim.analyze.model.VariableListEvent;
import com.cburch.logisim.analyze.model.VariableListListener;
import com.cburch.logisim.strings.StringUtil;
import com.cburch.logisim.util.Strings;

class VariableTab extends JPanel implements TabInterface {
    private static class VariableListModel extends AbstractListModel
            implements VariableListListener {
        private VariableList list;
        
        public VariableListModel(VariableList list) {
            this.list = list;
            list.addVariableListListener(this);
        }
        
        public int getSize() {
            return list.size();
        }
        
        public Object getElementAt(int index) {
            return list.get(index);
        }
        
        public void listChanged(VariableListEvent event) {
            int index;
            switch(event.getType()) {
            case VariableListEvent.ALL_REPLACED:
                fireContentsChanged(this, 0, getSize());
                return;
            case VariableListEvent.ADD:
                index = list.indexOf(event.getVariable());
                fireIntervalAdded(this, index, index);
                return;
            case VariableListEvent.REMOVE:
                index = ((Integer) event.getData()).intValue();
                fireIntervalRemoved(this, index, index);
                return;
            case VariableListEvent.MOVE:
                fireContentsChanged(this, 0, getSize());
                return;
            case VariableListEvent.REPLACE:
                index = ((Integer) event.getData()).intValue();
                fireContentsChanged(this, index, index);
                return;
            }
        }
    }
    
    private class MyListener
            implements ActionListener, DocumentListener, ListSelectionListener {
        public void actionPerformed(ActionEvent event) {
            Object src = event.getSource();
            if((src == add || src == field) && add.isEnabled()) {
                String name = field.getText().trim();
                if(!name.equals("")) {
                    data.add(name);
                    list.setSelectedValue(name, true);
                    field.setText("");
                    field.grabFocus();
                }
            } else if(src == rename) {
                String oldName = (String) list.getSelectedValue();
                String newName = field.getText().trim();
                if(oldName != null && !newName.equals("")) {
                    data.replace(oldName, newName);
                    field.setText("");
                    field.grabFocus();
                }
            } else if(src == remove) {
                String name = (String) list.getSelectedValue();
                if(name != null) data.remove(name);
            } else if(src == moveUp) {
                String name = (String) list.getSelectedValue();
                if(name != null) {
                    data.move(name, -1);
                    list.setSelectedValue(name, true);
                }
            } else if(src == moveDown) {
                String name = (String) list.getSelectedValue();
                if(name != null) {
                    data.move(name, 1);
                    list.setSelectedValue(name, true);
                }
            }
        }
        
        public void insertUpdate(DocumentEvent event) {
            computeEnabled();
        }
        public void removeUpdate(DocumentEvent event) { insertUpdate(event); }
        public void changedUpdate(DocumentEvent event) { insertUpdate(event); }

        public void valueChanged(ListSelectionEvent event) {
            computeEnabled();
        }
        
        public void listChanged(VariableListEvent event) {
            switch(event.getType()) {
            case VariableListEvent.ALL_REPLACED:
                list.setSelectedValue(null, false);
                break;
            case VariableListEvent.REMOVE:
                if(event.getVariable().equals(list.getSelectedValue())) {
                    int index = ((Integer) event.getData()).intValue();
                    if(index >= data.size()) {
                        if(data.isEmpty()) {
                            list.setSelectedValue(null, false);
                        }
                        index = data.size() - 1;
                    }
                    list.setSelectedValue(data.get(index), true);
                }
                break;
            case VariableListEvent.ADD:
            case VariableListEvent.MOVE:
            case VariableListEvent.REPLACE:
                break;
            }
            list.validate();
        }
    }
    
    private VariableList data;
    private MyListener myListener = new MyListener();
    
    private JList list = new JList();
    private JTextField field = new JTextField();
    private JButton remove = new JButton();
    private JButton moveUp = new JButton();
    private JButton moveDown = new JButton();
    private JButton add = new JButton();
    private JButton rename = new JButton();
    private JLabel error = new JLabel(" ");
    
    VariableTab(VariableList data) {
        this.data = data;
        
        list.setModel(new VariableListModel(data));
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        list.addListSelectionListener(myListener);
        remove.addActionListener(myListener);
        moveUp.addActionListener(myListener);
        moveDown.addActionListener(myListener);
        add.addActionListener(myListener);
        rename.addActionListener(myListener);
        field.addActionListener(myListener);
        field.getDocument().addDocumentListener(myListener);
        
        JScrollPane listPane = new JScrollPane(list,
            ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        listPane.setPreferredSize(new Dimension(100, 100));
        
        JPanel topPanel = new JPanel(new GridLayout(3, 1));
        topPanel.add(remove);
        topPanel.add(moveUp);
        topPanel.add(moveDown);
        
        JPanel fieldPanel = new JPanel();
        fieldPanel.add(rename);
        fieldPanel.add(add);
        
        GridBagLayout gb = new GridBagLayout();
        GridBagConstraints gc = new GridBagConstraints();
        setLayout(gb);
        Insets oldInsets = gc.insets;
        
          gc.insets = new Insets(10, 10, 0, 0);
          gc.fill = GridBagConstraints.BOTH;
          gc.weightx = 1.0;
        gb.setConstraints(listPane, gc); add(listPane);
        
          gc.fill = GridBagConstraints.NONE;
          gc.anchor = GridBagConstraints.PAGE_START;
          gc.weightx = 0.0;
        gb.setConstraints(topPanel, gc); add(topPanel);

          gc.insets = new Insets(10, 10, 0, 10);
          gc.gridwidth = GridBagConstraints.REMAINDER;
          gc.gridx = 0;
          gc.gridy = GridBagConstraints.RELATIVE;
          gc.fill = GridBagConstraints.HORIZONTAL;
        gb.setConstraints(field, gc); add(field);
        
          gc.insets = oldInsets;
          gc.fill = GridBagConstraints.NONE;
          gc.anchor = GridBagConstraints.LINE_END;
        gb.setConstraints(fieldPanel, gc); add(fieldPanel);
        
          gc.fill = GridBagConstraints.HORIZONTAL;
        gb.setConstraints(error, gc); add(error);
                
        if(!data.isEmpty()) list.setSelectedValue(data.get(0), true);
        computeEnabled();
    }
    
    void localeChanged() {
        remove.setText(Strings.get("variableRemoveButton", Strings.analyzeSource));
        moveUp.setText(Strings.get("variableMoveUpButton", Strings.analyzeSource));
        moveDown.setText(Strings.get("variableMoveDownButton", Strings.analyzeSource));
        add.setText(Strings.get("variableAddButton", Strings.analyzeSource));
        rename.setText(Strings.get("variableRenameButton", Strings.analyzeSource));
        validateInput();
    }
    
    void registerDefaultButtons(DefaultRegistry registry) {
        registry.registerDefaultButton(field, add);
    }
    
    private void computeEnabled() {
        String name = (String) list.getSelectedValue();
        int index = list.getSelectedIndex();
        remove.setEnabled(name != null);
        moveUp.setEnabled(name != null && index > 0);
        moveDown.setEnabled(name != null && index < data.size() - 1);

        boolean ok = validateInput();
        add.setEnabled(ok && data.size() < data.getMaximumSize());
        rename.setEnabled(ok && name != null);
    }
    
    private boolean validateInput() {
        String text = field.getText().trim();
        boolean ok = true;
        boolean errorShown = true;
        if(text.length() == 0) {
            errorShown = false;
            ok = false;
        } else if(!Character.isJavaIdentifierStart(text.charAt(0))) {
            error.setText(Strings.get("variableStartError", Strings.analyzeSource));
            ok = false;
        } else {
            for(int i = 1; i < text.length() && ok; i++) {
                char c = text.charAt(i);
                if(!Character.isJavaIdentifierPart(c)) {
                    error.setText(StringUtil.format(Strings.get("variablePartError", Strings.analyzeSource), "" + c));
                    ok = false;
                }
            }
        }
        if(ok) {
            for(int i = 0, n = data.size(); i < n && ok; i++) {
                String other = data.get(i);
                if(text.equals(other)) {
                    error.setText(Strings.get("variableDuplicateError", Strings.analyzeSource));
                    ok = false;
                }
            }
        }
        if(ok || !errorShown) {
            if(data.size() >= data.getMaximumSize()) {
                error.setText(StringUtil.format(Strings.get("variableMaximumError", Strings.analyzeSource),
                        "" + data.getMaximumSize()));
            } else {
                error.setText(" ");
            }
        }
        return ok;
    }

    public void copy() {
        field.requestFocus();
        field.copy();
    }

    public void paste() {
        field.requestFocus();
        field.paste();
    }

    public void delete() {
        field.requestFocus();
        field.replaceSelection("");
    }

    public void selectAll() {
        field.requestFocus();
        field.selectAll();
    }
}
